/**************************************************************************
 * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
 *                                                                        *
 * Author: The ALICE Off-line Project.                                    *
 * Contributors are mentioned in the code where appropriate.              *
 *                                                                        *
 * Permission to use, copy, modify and distribute this software and its   *
 * documentation strictly for non-commercial purposes is hereby granted   *
 * without fee, provided that the above copyright notice appears in all   *
 * copies and that both the copyright notice and this permission notice   *
 * appear in the supporting documentation. The authors make no claims     *
 * about the suitability of this software for any purpose. It is          *
 * provided "as is" without express or implied warranty.                  *
 **************************************************************************/

/* $Id: AliAnalysisTaskESDMuonFilterO.cxx 57113 2012-06-12 08:52:42Z fca $ */

//
// Add the muon tracks to the generic AOD track branch during the 
// filtering of the ESD. 
//
// Authors: R. Arnaldi 5/5/08 and L. Aphecetche January 2011
//
// Note that we :
//   - completely disable all the branches that are not required by (most) the muon analyses,
//     e.g. cascades, v0s, kinks, jets, etc...
//   - filter severely the tracks (keep only muon tracks) and vertices (keep only primary -including
//     pile-up - vertices) branches 
// 
// (see AddFilteredAOD method)
//

#include "AliAnalysisTaskESDMuonFilterO.h"

#include "AliAODDimuon.h"
#include "AliAODEvent.h"
#include "AliAODHandler.h"
#include "AliAODExtension.h"
#include "AliAODMCParticle.h"
#include "AliAODMuonReplicator0.h"
#include "AliAODVertex.h"
#include "AliAnalysisFilter.h"
#include "AliAnalysisManager.h"
#include "AliCodeTimer.h"
#include "AliESDEvent.h"
#include "AliESDInputHandler.h"
#include "AliESDMuonTrack.h"
#include "AliESDVertex.h"
#include "AliESDtrack.h"
#include "AliLog.h"
#include "AliMCEvent.h"
#include "AliMCEventHandler.h"
#include "AliMultiplicity.h"
#include <TChain.h>
#include <TFile.h>
#include <TParticle.h>
#include "AliESDtrackCuts.h"
#include "AliAODTrack.h"
#include "AliAODPid.h"
#include "TF1.h"
#include "AliPIDResponse.h"

using std::cout;
using std::endl;
ClassImp(AliAnalysisTaskESDMuonFilterO)
ClassImp(AliAnalysisNonMuonTrackCuts)

////////////////////////////////////////////////////////////////////////

AliAnalysisNonMuonTrackCuts::AliAnalysisNonMuonTrackCuts()
{
  // default ctor 
}

Bool_t AliAnalysisNonMuonTrackCuts::IsSelected(TObject* obj)
{
  // Returns true if the object is a muon track
  AliAODTrack* track = dynamic_cast<AliAODTrack*>(obj);

  ULong_t  status;

  if(track){
    
    status  = (ULong_t)track->GetStatus();

    if(track->GetTPCNcls() > 80 &&
       track->Chi2perNDF() < 5  &&
       track->IsOn(AliAODTrack::kTPCrefit) &&
       track->IsOn(AliAODTrack::kTPCin)    &&
       !track->IsOn(AliAODTrack::kITSpureSA))
      {
	return kTRUE;
      }
  } 
  
  else 
    return kFALSE;
  

}

AliAnalysisNonPrimaryVertices::AliAnalysisNonPrimaryVertices()
{
  // default ctor   
}

Bool_t AliAnalysisNonPrimaryVertices::IsSelected(TObject* obj)
{
  // Returns true if the object is a primary vertex
  
  AliAODVertex* vertex = dynamic_cast<AliAODVertex*>(obj);
  if (vertex)
    {
      if ( vertex->GetType() == AliAODVertex::kPrimary     ||
	   vertex->GetType() == AliAODVertex::kMainSPD     ||
	   vertex->GetType() == AliAODVertex::kPileupSPD   ||
	   vertex->GetType() == AliAODVertex::kPileupTracks||
	   vertex->GetType() == AliAODVertex::kMainTPC )
	{
	  return kTRUE;
	}
    }
  
  //  enum AODVtx_t {kUndef=-1, kPrimary, kKink, kV0, kCascade, kMulti, kMainSPD, kPileupSPD, kPileupTracks,kMainTPC};

  return kFALSE;
  
}

AliAnalysisTaskESDMuonFilterO::AliAnalysisTaskESDMuonFilterO(Bool_t onlyMuon, Bool_t keepAllEvents, Int_t mcMode, Int_t nsigmaTrk1,Int_t nsigmaTrk2, Int_t partType1,Int_t partType2):
  AliAnalysisTaskSE(),
  fTrackFilter(0x0),
  fEnableMuonAOD(kTRUE),
  fEnableDimuonAOD(kTRUE),
  fOnlyMuon(onlyMuon),
  fKeepAllEvents(keepAllEvents),
  fMCMode(mcMode),
  fnSigmaTrk1(nsigmaTrk1),
  fnSigmaTrk2(nsigmaTrk2),
  fpartType1(partType1),
  fpartType2(partType2),
  fPIDResponse(0)
{
  // Default constructor
}

AliAnalysisTaskESDMuonFilterO::AliAnalysisTaskESDMuonFilterO(const char* name, Bool_t onlyMuon, Bool_t keepAllEvents, Int_t mcMode, Int_t nsigmaTrk1,Int_t nsigmaTrk2, Int_t partType1,Int_t partType2):
  AliAnalysisTaskSE(name),
  fTrackFilter(0x0),
  fEnableMuonAOD(kTRUE),
  fEnableDimuonAOD(kTRUE),
  fOnlyMuon(onlyMuon),
  fKeepAllEvents(keepAllEvents),
  fMCMode(mcMode),
  fnSigmaTrk1(nsigmaTrk1),
  fnSigmaTrk2(nsigmaTrk2),
  fpartType1(partType1),
  fpartType2(partType2),
  murep(0),
  fPIDResponse(0)
{
  // Constructor
}

//______________________________________________________________________________
void AliAnalysisTaskESDMuonFilterO::UserCreateOutputObjects()
{
  //-----------------------------------------------
  // Particle Identification Setup (new PID object)
  //-----------------------------------------------
  AliAnalysisManager *man=AliAnalysisManager::GetAnalysisManager();
  cout<<"===========================================Manager: "<<man<<endl;
  AliInputEventHandler* inputHandler = (AliInputEventHandler*) (man->GetInputEventHandler());
  cout<<"ih: "<<inputHandler<<endl;
  
  fPIDResponse = inputHandler->GetPIDResponse();

  // Create the output container
  if (fTrackFilter) OutputTree()->GetUserInfo()->Add(fTrackFilter);
  cout<<"Sotto"<<endl;
}

//______________________________________________________________________________
void AliAnalysisTaskESDMuonFilterO::PrintTask(Option_t *option, Int_t indent) const
{
  // Specify how we are configured
  
  AliAnalysisTaskSE::PrintTask(option,indent);
  
  TString spaces(' ',indent+3);
  
  if ( fOnlyMuon ) 
    {
      cout << spaces.Data() << "Keep only muon information " << endl;        
    }
  else 
    {
      cout << spaces.Data() << "Keep all information from standard AOD" << endl;
    }
  
  if ( fKeepAllEvents ) 
    {
      cout << spaces.Data() << "Keep all events, regardless of number of muons" << endl;    
    }
  else 
    {
      cout << spaces.Data() << "Keep only events with at least one muon" << endl;
    }
  
  if ( fMCMode > 0 ) 
    {
      cout << spaces.Data() << "Assuming work on MC data (i.e. will transmit MC branches)" << endl;
    }
}

//______________________________________________________________________________
void AliAnalysisTaskESDMuonFilterO::AddFilteredAOD(const char* aodfilename, const char* title)
{
  //  AliAODHandler *aodH = (AliAODHandler*)((AliAnalysisManager::GetAnalysisManager())->GetInputEventHandler());
  AliAODHandler *aodH = (AliAODHandler*)((AliAnalysisManager::GetAnalysisManager())->GetOutputEventHandler());
  if (!aodH) Fatal("UserCreateOutputObjects", "No AOD handler");
  cout<<"Add Filterd AOD "<<aodH->AddFilteredAOD(aodfilename,title)<<endl;
  AliAODExtension* ext = aodH->AddFilteredAOD(aodfilename,title);
  
  cout<<"Handle inside add filterAOD: "<<aodH<<endl;

  cout<<"ext: "<<ext<<endl;
  
  if (!ext) return;
  
  if ( fOnlyMuon ) 
    {    
    
      cout<<"Inside fonly muon: "<<endl;

      // AliAODMuonReplicator* murep = new AliAODMuonReplicator("MuonReplicator",
      //                                                        "remove non muon tracks and non primary or pileup vertices",
      //                                                        new AliAnalysisNonMuonTrackCuts,
      //                                                        new AliAnalysisNonPrimaryVertices,
      //                                                        fMCMode);
      
      // AliAODMuonReplicator0* murep = new AliAODMuonReplicator0("MuonReplicator0",
      // 							       "remove non interesting tracks",
      // 							       new AliAnalysisNonMuonTrackCuts,
      // 							       new AliAnalysisNonPrimaryVertices,
      // 							       fMCMode);
      if(!murep)delete murep;

      murep = new AliAODMuonReplicator0("MuonReplicator0",
      					"remove non interesting tracks",
      					new AliAnalysisNonMuonTrackCuts,
      					new AliAnalysisNonPrimaryVertices,
      					fMCMode,fnSigmaTrk1,fnSigmaTrk2,fpartType1,fpartType2);

  
     

      cout<<"murep: "<<murep<<endl;
      
      ext->DropUnspecifiedBranches(); // all branches not part of a FilterBranch call (below) will be dropped
      
      ext->FilterBranch("header",murep);    
      ext->FilterBranch("tracks",murep);    
      ext->FilterBranch("vertices",murep);  
      ext->FilterBranch("dimuons",murep); //per test
      ext->FilterBranch("AliAODVZERO",murep);
      ext->FilterBranch("AliAODTZERO",murep);
      
      cout<<"add filterd aod"<<endl;
      
      if ( fMCMode > 0 ) 
	{
	  // MC branches will be copied (if present), as they are, but only
	  // for events with at least one muon. 
	  // For events w/o muon, mcparticles array will be empty and mcheader will be dummy
	  // (e.g. strlen(GetGeneratorName())==0)
	  
	  ext->FilterBranch("mcparticles",murep);
	  ext->FilterBranch("mcHeader",murep);
	}
    }  
  
  cout<<"fine add filterd"<<endl;
}

//______________________________________________________________________________
void AliAnalysisTaskESDMuonFilterO::Init()
{

  cout<<"%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Sono in INIT"<<endl;
  // Initialization
  if(fEnableMuonAOD) 
    AddFilteredAOD("AliAOD.Muons.root", "MuonEvents");
  cout<<"Fine INIT"<<endl;
  //  if(fEnableDimuonAOD) AddFilteredAOD("AliAOD.Dimuons.root", "DimuonEvents");    
}


//______________________________________________________________________________
void AliAnalysisTaskESDMuonFilterO::UserExec(Option_t */*option*/)
{
  // Execute analysis for current event					    
  
  cout<<">>>>>>>>>>>>Inside User exec<<<<<<<<<<<<<<<<<"<<endl;

  Long64_t ientry = Entry();
  if(fDebug)printf("Muon Filter: Analysing event # %5d\n", (Int_t) ientry);
  cout<<"--------------->Enter the user exec<------------------------------"<<endl;
  
  //Check the PIDresponse
  if(!fPIDResponse) {
    AliError("Cannot get pid response");
    return;
  }

  //***************************************************

  ConvertESDtoAOD();

  //*************************************************
  
  cout<<"-------------------------------------------------------------------FINE ESD TO AOD CONVERTER!!!"<<endl;
  
  // if(!murep)
  //   delete murep;
  
}
//_____________________________________________________________________________
Bool_t AliAnalysisTaskESDMuonFilterO::IsTrackAccepted(AliESDtrack * const track)
{
  
  //! TPC
  
  Int_t    minclsTPC=60;
  //  Int_t    minclsTPC=10;
  Double_t maxchi2perTPCcl=5.;
  
  AliESDtrackCuts *fEsdTrackCuts = new AliESDtrackCuts("esdtrackCuts");
  fEsdTrackCuts->SetRequireITSStandAlone(kFALSE);
  fEsdTrackCuts->SetRequireITSPureStandAlone(kFALSE);
  //  fEsdTrackCuts->SetRequireITSRefit(kTRUE);

  fEsdTrackCuts->SetRequireTPCRefit(kTRUE);
  fEsdTrackCuts->SetAcceptKinkDaughters(kFALSE);
  fEsdTrackCuts->SetMinNClustersTPC(minclsTPC);
  fEsdTrackCuts->SetMaxChi2PerClusterTPC(maxchi2perTPCcl);

  Bool_t IsTrackAccepted =  fEsdTrackCuts->AcceptTrack(track);
  if (IsTrackAccepted) 
    return kTRUE;
 
}

//_____________________________________________________________________________
Bool_t AliAnalysisTaskESDMuonFilterO::IsHighDeDxParticle(AliESDtrack * track) {
  
  //
  // check if particle is Z > 1 
  //

  // if (track->GetTPCNcls() < 60) 
  //   return kFALSE;
  
  Double_t mom = track->GetTPCmomentum();
  //track->GetInnerParam()->GetP(); //solitamente usato
  
  if (mom < 0.2) return kFALSE; // protection against unexpected behavior of Aleph parameterization
  Float_t dca[2], bCov[3];
  track->GetImpactParameters(dca,bCov);

  //Double_t triggerDeDx = fPIDResponse->NumberOfSigmasTPC(track,(AliPID::EParticleType)7);
  
  
  Double_t triggerDeDx =4*AliExternalTrackParam::BetheBlochAleph((mom*2)/(0.938*3.4),1.0288,31.9806,5.04114e-11,2.13096,2.38541);
    
    //  if (triggerDeDx > -3 && track->GetTPCsignal() < 1000 /*&& TMath::Abs(dca[0])<3.*/) 
 
  if (track->GetTPCsignal() > triggerDeDx && track->GetTPCsignal()<5000 /*&& TMath::Abs(dca[0])<3.*/) 
    return kTRUE;
  
  return kFALSE;
} 

//______________________________________________________________________________
void AliAnalysisTaskESDMuonFilterO::SetAODPID(AliESDtrack *esdtrack, AliAODTrack *aodtrack, AliAODPid *detpid)
{
  //
  // Setter for the raw PID detector signals
  //

  if(!aodtrack->GetDetPid()){// prevent memory leak when calling SetAODPID twice for the same track
    detpid = new AliAODPid();
    SetDetectorRawSignals(detpid,esdtrack);
    aodtrack->SetDetPID(detpid);
  }
}

//______________________________________________________________________________
void AliAnalysisTaskESDMuonFilterO::SetDetectorRawSignals(AliAODPid *aodpid, AliESDtrack *track)
{
//
//assignment of the detector signals (AliXXXesdPID inspired)
//
  if(!track){
    AliInfo("no ESD track found. .....exiting");
    return;
  }
  
  // TPC momentum
  aodpid->SetTPCmomentum(track->GetTPCmomentum());
  // aodpid->SetTPCTgl(track->GetTPCTgl());

  // aodpid->SetITSsignal(track->GetITSsignal());
  // Double_t itsdedx[4]; // dE/dx samples for individual ITS layers
  // track->GetITSdEdxSamples(itsdedx);
  // aodpid->SetITSdEdxSamples(itsdedx);

  aodpid->SetTPCsignal(track->GetTPCsignal());
  aodpid->SetTPCsignalN(track->GetTPCsignalN());
  
  // if(track->GetTPCdEdxInfo()) 
  //   aodpid->SetTPCdEdxInfo(track->GetTPCdEdxInfo());

  //---fin qui
  
  // //n TRD planes = 6
  // Int_t nslices = track->GetNumberOfTRDslices()*6;
  // TArrayD trdslices(nslices);
  // for(Int_t iSl =0; iSl < track->GetNumberOfTRDslices(); iSl++) {
  //   for(Int_t iPl =0; iPl<6; iPl++) trdslices[iPl*track->GetNumberOfTRDslices()+iSl] = track->GetTRDslice(iPl,iSl);
  // }
 
  // //TRD momentum
  // for(Int_t iPl=0;iPl<6;iPl++){
  //   Double_t trdmom=track->GetTRDmomentum(iPl);
  //   aodpid->SetTRDmomentum(iPl,trdmom);
  // }
  
  // aodpid->SetTRDslices(track->GetNumberOfTRDslices()*6,trdslices.GetArray());
  // aodpid->SetTRDsignal(track->GetTRDsignal());
  
  // //TRD clusters and tracklets
  // aodpid->SetTRDncls(track->GetTRDncls());
  // aodpid->SetTRDntrackletsPID(track->GetTRDntrackletsPID());
  
  // aodpid->SetTRDChi2(track->GetTRDchi2());
  
  // //TOF PID  
  // Double_t times[AliPID::kSPECIES]; track->GetIntegratedTimes(times);
  // aodpid->SetIntegratedTimes(times);
  
  // aodpid->SetTOFsignal(track->GetTOFsignal());
  
  // Double_t tofRes[5];
  // for (Int_t iMass=0; iMass<5; iMass++){
  //   tofRes[iMass]=0; //backward compatibility
  // }
  // aodpid->SetTOFpidResolution(tofRes);
  
}



//---- Funziona


void AliAnalysisTaskESDMuonFilterO::ConvertESDtoAOD() 

{
  cout<<"CONVERT ESD TO AOD"<<endl;

  
  //RAMONA!!!!!!!!!!!!!!!!!!!!!

  AliVEvent *event = InputEvent();
  cout<<"VEvent: "<<event<<endl;
  AliAODEvent *lAODevent=(AliAODEvent*)InputEvent();
  cout<<"AOD Event: "<<event<<endl;
  AliAODHeader* header =lAODevent->GetHeader();
  cout<<"header :"<<header<<endl;
  Int_t jTracks =  lAODevent->GetNumberOfTracks();
  cout<<"n jtracks :"<<jTracks<<endl;

  // Read primary vertex from AOD event 
  // AliAODVertex *primary = *(AODEvent()->GetPrimaryVertex());

  AliAODVertex *primary = lAODevent->GetPrimaryVertex();
  if (fDebug && primary) primary->Print();
  cout<<"Primary vtx x: "<<primary->GetX()<<" "<<primary->GetY()<<" "<<primary->GetZ()<<endl;
  
    
  AliAODHandler* handler = dynamic_cast<AliAODHandler*>(AliAnalysisManager::GetAnalysisManager()->GetOutputEventHandler());
  //AliAODHandler* handler = dynamic_cast<AliAODHandler*>(AliAnalysisManager::GetAnalysisManager()->GetInputEventHandler());

  cout<<"Mag field Filer: "<<lAODevent->GetMagneticField()<<endl;

  // lAODevent->Print();

  cout<<"handler inside convert "<<handler<<endl;
  if ( handler ){
    AliAODExtension *extMuons = handler->GetFilteredAOD("AliAOD.Muons.root");
    //  AliAODExtension *extMuons = handler->GetFilteredAOD("../1/pass2/AliAOD.root");
   if ( extMuons ) {				
     //   extMuons->Init("");
     extMuons->SetEvent(lAODevent);
     extMuons->SelectEvent();
     extMuons->Print();
     extMuons->FinishEvent();
   }
  }


}
//------------------------------------------------
void AliAnalysisTaskESDMuonFilterO::Terminate(Option_t */*option*/)
{
  // Terminate analysis
  //
  //  delete murep;
  
  if (fDebug > 1) printf("AnalysisESDfilter: Terminate() \n");
}
